import { onBeforeUnmount } from 'vue'

class Tio {
  /**
   * @param protocol
   * @param {*} ip
   * @param {*} port
   * @param {Array} handlers 回调事件句柄的集合
   * @param {*} heartbeatTimeout 心跳时间 单位：毫秒
   * @param {*} reconnInterval 重连间隔时间 单位：毫秒
   * @param {*} binaryType 'blob' or 'arraybuffer';//arraybuffer是字节
   */

  isconnect = false
  handlers = []
  constructor(send) {
    this.send = send
  }
  tio(protocol, ip, port, handler, heartbeatTimeout, reconnInterval, binaryType, reconnCount) {
    this.ip = ip
    this.port = port
    if (port) {
      this.url = protocol + '://' + ip + ':' + port
    } else {
      this.url = protocol + '://' + ip
    }
    this.binaryType = binaryType || 'arraybuffer'
    this.reconnUrl = this.url

    if (handler instanceof Array) {
      this.handlers = [...this.handlers, ...handler]
    } else if (handler) {
      this.handlers.push(handler)
    }
    this.heartbeatTimeout = heartbeatTimeout
    this.reconnInterval = reconnInterval
    this.count = 0
    this.reconnCount = reconnCount

    this.isClose = false //关闭ws时使用

    this.lastInteractionTime = function () {
      if (arguments.length === 1) {
        this.lastInteractionTimeValue = arguments[0]
      }
      return this.lastInteractionTimeValue
    }

    this.heartbeatSendInterval = heartbeatTimeout / 2
  }
  //添加事件句柄
  addEventHandler(handler) {
    this.handlers.push(handler)
  }
  delEventHandler(handler) {
    let idx = this.handlers.indexOf(handler)
    if (idx != -1) {
      this.handlers.splice(idx, 1)
    }
  }
  // 启动连接
  connect(isReconnect) {
    let _url = this.url
    if (isReconnect) {
      _url = this.reconnUrl
    }
    const ws = new WebSocket(_url)
    this.ws = ws
    const self = this

    ws.binaryType = this.binaryType // 'arraybuffer'; // 'blob' or 'arraybuffer';//arraybuffer是字节

    ws.onopen = function (event) {
      self.count = 0 //重连计数归零
      self.isconnect = true
      console.log(`ws连接成功!url:${_url}`)
      self.touchEvent(event, 'onopen', ws)
      self.lastInteractionTime(new Date().getTime())

      for (let key in self.handlers) {
        let handler = self.handlers[key]
        if (handler.ping instanceof Function) {
          self.pingIntervalId = setInterval(function () {
            handler.ping(self)
          }, self.heartbeatSendInterval)
          break
        }
      }
    }
    ws.onmessage = function (event) {
      self.touchEvent(event, 'onmessage', ws)
      self.lastInteractionTime(new Date().getTime())
    }
    ws.onclose = function (event) {
      console.log("ws已关闭!")
      if (self.pingIntervalId) {
        clearInterval(self.pingIntervalId) // clear send heartbeat task
      }
      this.isconnect = false
      try {
        self.touchEvent(event, 'onclose', ws)

      } catch (error) {
      }
      if (false === self.isClose) {
        self.reconn(event)
      }
    }
    ws.onerror = function (event) {
      self.touchEvent(event, 'onerror', ws)
    }
    return ws
  }

  touchEvent(event, eventType, ws) {
    this.handlers.forEach((handler) => {
      if (handler[eventType] instanceof Function) {
        handler[eventType](event, ws)
      }
    })
  }

  reconn() {
    const self = this
    self.count++
    let timer = setTimeout(function () {
      if (self.count > self.reconnCount) {
        clearInterval(timer)
        timer = null
        self.count = 0
        return
      }
      const ws = self.connect(true)
      self.ws = ws
    }, self.reconnInterval)
  }

  ping() {
    //test 需要测试
    const iv = new Date().getTime() - this.lastInteractionTime() // 已经多久没发消息了
    // 单位：秒
    this.handlers.forEach((handler) => {
      if ((this.heartbeatSendInterval + iv) >= this.heartbeatTimeout) {
        handler.ping(this.ws)
      }
    })

  }

  dispose() {
    this.isClose = true
    if (this.ws) {
      this.ws.close()
    }
    this.ws = null
  }

}
class Tiows {
  constructor(ip, port, reconnCount, handler, ws_protocol = 'ws') {
    this.ws_protocol = ws_protocol // ws 或 wss
    this.heartbeatTimeout = 60000// 心跳超时时间，单位：毫秒
    this.reconnInterval = 1000 // 重连间隔时间，单位：毫秒
    this.binaryType = 'blob'// 'blob' or 'arraybuffer';//arraybuffer是字节
    this.tiows = new Tio(this)
    this.initWs(ip, port, reconnCount, handler)
    this.handlers = this.tiows.handlers
    this.handlers.push(this)
    this.type = 'tiows'
    onBeforeUnmount(() => {
      this.close()
    })
  }

  /**
  * 初始化
  * @param {Number} reconnCount 断开链接后，尝试重连的次数
  * @param {Object} handler 包含ws几个事件回调函数的对象
  * @returns
  */
  initWs(ip, port, reconnCount, handler) {
    this.tiows.tio(
      this.ws_protocol,
      ip,
      port,
      handler,
      this.heartbeatTimeout,
      this.reconnInterval,
      this.binaryType,
      reconnCount
    )
    this.tiows.connect()
  }

  // 发送消息
  send(msg) {
    this.tiows.ws.send(msg)
  }

  isconnect() {
    return this.tiows.isconnect
  }

  /**
  * @brief 0表示正在连接；1表示连接成功，可以通信了；2表示正在关闭；3表示已经关闭或者连接失败。 
  * @returns {Number} 
  */
  getState() {
    return this.tiows.ws.readyState
  }
  connect() {
    if (!this.isconnect) {
      this.tiows.connect()
    }
  }

  //添加事件的句柄
  addEventHandler(handler) {
    this.tiows.addEventHandler(handler)
    onBeforeUnmount(() => {
      this.delEventHandler(handler)
    })
  }

  //删除事件句柄
  delEventHandler(handler) {
    this.tiows.delEventHandler(handler)
  }

  //释放ws相关数据
  close() {
    console.log('tiows close!')
    this.tiows.dispose()
    this.tiows = null
  }
}

export default Tiows